// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using svs.Mobile.BeyondMedia.FeedCore.RSS.Entities;

namespace svs.Mobile.BeyondMedia.FeedCore.RSS
{
	public class ItemReadHistory
	{
		private static Dictionary<Guid, List<int>> m_history;
		private static int m_modificationChangeCount;
		
		private static Dictionary<Guid, List<int>> History
		{
			get
			{
				if (m_history == null)
					LoadHistory();

				return m_history;
			}
		}

		public static bool HasEntryFor(Guid feedID, int itemID)
		{
			return History.ContainsKey(feedID) && History[feedID].Contains(itemID);
		}

		public static void AddItem(Guid feedID, int itemID)
		{
			if (HasEntryFor(feedID, itemID))
				return;

			if (!History.ContainsKey(feedID))
			{
				History[feedID] = new List<int>();
			}

			History[feedID].Add(itemID);
			MarkModified();
			AutoSaveHistory();
		}

		public static void RemoveItem(Guid feedID, int itemID)
		{
			if (!HasEntryFor(feedID, itemID))
				return;

			History[feedID].Remove(itemID);
			MarkModified();
			AutoSaveHistory();
		}

		public static void RemoveAllItemsFor(Guid feedID)
		{
			if (!History.ContainsKey(feedID))
				return;

			History.Remove(feedID);
			MarkModified();
		}

		public static void ClearHistory()
		{
			History.Clear();
			MarkModified();
		}

		/// <summary>
		/// Removes all records that are in the history but are not in the feed any more
		/// </summary>
		/// <param name="feed"></param>
		public static void PurgeOldRecordsForFeed(RssFeed feed)
		{
			if (feed == null)
				throw new ArgumentNullException("feed");

			if(History.ContainsKey(feed.FeedID))
			{
				var toRemove = new List<int>();
				foreach (var i in History[feed.FeedID])
				{
					if(feed.GetItemById(i) == null)
						toRemove.Add(i);
				}

				foreach (int i in toRemove)
				{
					RemoveItem(feed.FeedID,i);
				}
			}
		}

		public static void SaveHistory()
		{
			if (m_modificationChangeCount == 0)
				return;

			//Check if another thread is already saving
			if (!Monitor.TryEnter(m_history))
				return;

			try
			{
				CoreHelper.WriteTraceEntry("Saving Item History...");
				string tempPath = Configuration.ItemHistoryFilePath + ".tmp";

				FileStream fs = null;
				try
				{
					fs = File.OpenWrite(tempPath);
					var bw = new BinaryWriter(fs);

					foreach (var entry in History)
					{
						foreach (var item in entry.Value)
						{
							bw.Write(entry.Key.ToByteArray());
							bw.Write(item);
						}
					}
					fs.Close();
				}
				catch (Exception ex)
				{
					CoreHelper.WriteLogEntry("Unable to save item history. Reason: " + ex.Message);
				}
				finally
				{
					if (fs != null)
						fs.Close();
				}

				//Rename the temp file
				try
				{
					File.Copy(tempPath, Configuration.ItemHistoryFilePath, true);
					File.Delete(tempPath);
					m_modificationChangeCount = 0;
				}
				catch (Exception ex)
				{
					CoreHelper.WriteLogEntry("Unable to copy item history! " + ex.Message);
				}
			}
			finally
			{
				Monitor.Exit(m_history);
			}
		}

		public static void AddAllItems(RssFeed feed)
		{
			if(feed == null)
				throw new ArgumentNullException("feed");

			RemoveAllItemsFor(feed.FeedID);

			foreach (RssFeedItem item in feed.Items)
			{
				if (item.ItemID.HasValue)
					AddItem(item.OriginatingFeedID, item.ItemID.Value);
			}

			AutoSaveHistory();
		}

		private static void LoadHistory()
		{
			FileStream fs = null;
			m_history = new Dictionary<Guid, List<int>>();

			if (File.Exists(Configuration.ItemHistoryFilePath))
			{
				try
				{
					fs = File.OpenRead(Configuration.ItemHistoryFilePath);
					var br = new BinaryReader(fs);

					while (br.PeekChar() != -1)
					{
						Guid id = Guid.Empty;
						var feedid = new byte[16];

						if (br.Read(feedid, 0, 16) == 16)
						{
							id = new Guid(feedid);
							if (!m_history.ContainsKey(id))
								m_history[id] = new List<int>();
						}

						int value = br.ReadInt32();

						if (id != Guid.Empty)
							m_history[id].Add(value);
					}

					fs.Close();
				}
				catch (Exception ex)
				{
					CoreHelper.WriteLogEntry("Unable to load item read history. Reason: " + ex.Message);
				}
				finally
				{
					if (fs != null)
						fs.Close();
				}
			}

			m_modificationChangeCount = 0;
		}

		private static void MarkModified()
		{
			m_modificationChangeCount++;
		}

		private static void AutoSaveHistory()
		{
			//Save every 5th modification
			if(m_modificationChangeCount > 5)	
				ThreadPool.QueueUserWorkItem(o => SaveHistory());
		}
	}
}